/**
 * @Author: dn-jinmin
 * @File:  tlog
 * @Version: 1.0.0
 * @Date: 2024/3/7
 * @Description:
 */

package tlog

import (
	"context"
	"encoding/json"
	"fmt"
	"strconv"
	"time"
)

type LogContext struct{}

func TraceStart(ctx context.Context) context.Context {
	return context.WithValue(ctx, LogContext{}, TraceId())
}

func TraceStartSetTraceId(ctx context.Context, traceId string) context.Context {
	return context.WithValue(ctx, LogContext{}, traceId)
}

func GetTraceId(ctx context.Context) (traceId string) {
	traceId, ok := ctx.Value(LogContext{}).(string)
	if ok {
		return traceId
	}
	return
}

type skipIDKey struct{}

func WithSkip(ctx context.Context, val int) context.Context {
	return context.WithValue(context.Background(), skipIDKey{}, val)
}

func TraceId() string {
	return RandId()
}

var defaultLog *log

type log struct {
	loggers       []LoggerWriter
	label         string
	mode          LogMod
	logWriteLimit int
	limit         chan struct{}
}

func NewLog(opts ...Options) *log {
	opt := newOptions(opts...)
	return &log{
		loggers:       opt.LoggerWriter,
		label:         opt.Label,
		mode:          opt.Mode,
		logWriteLimit: opt.LogWriteLimit,
		limit:         make(chan struct{}, opt.LogWriteLimit),
	}
}

// 初始化logx
func Init(opts ...Options) {
	defaultLog = NewLog(opts...)
}

// 日志刷新
func (l *log) Write(ctx context.Context, level string, relatedId, msg string, fields ...Field) {
	if !l.shallLog(level) {
		return
	}

	traceId, ok := ctx.Value(LogContext{}).(string)
	if !ok {
		traceId = TraceId()
	}

	var path string
	skip := 4
	if val, ok := ctx.Value(skipIDKey{}).(int); ok {
		skip = val // 从上下文中获取skip值，默认为2，即跳过2层调用栈，即logEntry函数的调用者
	}
	f, ok := getCallerFrame(skip)
	if ok {
		path = fmt.Sprintf("%s:%v", f.File, f.Line)
	}

	content := &Content{
		TraceId:   traceId,
		Path:      path,
		RelatedId: relatedId,
		Msg:       msg,
		Label:     l.label,
		Level:     level,
	}

	for i, _ := range l.loggers {
		l.limit <- struct{}{}
		// 刷新 或 记录
		go func(w LoggerWriter) {
			w.Write(ctx, content, fields...)
			<-l.limit
		}(l.loggers[i])
	}
}

// 日志是否写入
func (l *log) shallLog(level string) bool {
	switch l.mode {
	case All, DEV:
		return true
	case PROD:
		if level != DEBUG {
			return true
		}
	}
	return false
}

// ------- error log --------------

func Errorf(relatedId, format string, a ...any) {
	defaultLog.Write(context.Background(), ERROR, relatedId, fmt.Sprintf(format, a...))
}

func Error(relatedId string, msg interface{}, fields ...Field) {
	defaultLog.Write(context.Background(), ERROR, relatedId, MsgMarshal(msg), fields...)
}

func ErrorfCtx(ctx context.Context, relatedId, format string, a ...any) {
	defaultLog.Write(ctx, ERROR, relatedId, fmt.Sprintf(format, a...))
}

func ErrorCtx(ctx context.Context, relatedId string, msg interface{}, fields ...Field) {
	defaultLog.Write(ctx, ERROR, relatedId, MsgMarshal(msg), fields...)
}

// ------- info log --------------

func Info(relatedId string, msg interface{}, fields ...Field) {
	defaultLog.Write(context.Background(), INFO, relatedId, MsgMarshal(msg), fields...)
}

func Infof(relatedId, format string, a ...any) {
	defaultLog.Write(context.Background(), INFO, relatedId, fmt.Sprintf(format, a...))
}

func InfoCtx(ctx context.Context, relatedId string, msg interface{}, fields ...Field) {
	defaultLog.Write(ctx, INFO, relatedId, MsgMarshal(msg), fields...)
}

func InfofCtx(ctx context.Context, relatedId, format string, a ...any) {
	defaultLog.Write(ctx, INFO, relatedId, fmt.Sprintf(format, a...))
}

// ----------- debug log ---------------

func Debug(relatedId string, msg interface{}, fields ...Field) {
	defaultLog.Write(context.Background(), DEBUG, relatedId, MsgMarshal(msg), fields...)
}

func Debugf(relatedId, format string, a ...any) {
	defaultLog.Write(context.Background(), DEBUG, relatedId, fmt.Sprintf(format, a...))
}

func DebugCtx(ctx context.Context, relatedId string, msg interface{}, fields ...Field) {
	defaultLog.Write(ctx, DEBUG, relatedId, MsgMarshal(msg), fields...)
}

func DebugfCtx(ctx context.Context, relatedId, format string, a ...any) {
	defaultLog.Write(ctx, DEBUG, relatedId, fmt.Sprintf(format, a...))
}

// ----------- slow log ---------------

func Slow(relatedId string, msg interface{}, fields ...Field) {
	defaultLog.Write(context.Background(), SLOW, relatedId, MsgMarshal(msg), fields...)
}

func Slowf(relatedId, format string, a ...any) {
	defaultLog.Write(context.Background(), SLOW, relatedId, fmt.Sprintf(format, a...))
}

func SlowCtx(ctx context.Context, relatedId string, msg interface{}, fields ...Field) {
	defaultLog.Write(ctx, SLOW, relatedId, MsgMarshal(msg), fields...)
}

func SlowfCtx(ctx context.Context, relatedId, format string, a ...any) {
	defaultLog.Write(ctx, SLOW, relatedId, fmt.Sprintf(format, a...))
}

// ----------- Stat log ---------------

func Stat(relatedId string, msg interface{}, fields ...Field) {
	defaultLog.Write(context.Background(), STAT, relatedId, MsgMarshal(msg), fields...)
}

func Statf(relatedId, format string, a ...any) {
	defaultLog.Write(context.Background(), STAT, relatedId, fmt.Sprintf(format, a...))
}

func StatCtx(ctx context.Context, relatedId string, msg interface{}, fields ...Field) {
	defaultLog.Write(ctx, STAT, relatedId, MsgMarshal(msg), fields...)
}

func StatfCtx(ctx context.Context, relatedId, format string, a ...any) {
	defaultLog.Write(ctx, STAT, relatedId, fmt.Sprintf(format, a...))
}

// ----------- fatal log ---------------

func Fatal(relatedId string, msg interface{}, fields ...Field) {
	defaultLog.Write(context.Background(), FATAL, relatedId, MsgMarshal(msg), fields...)
}

func Fatalf(relatedId, format string, a ...any) {
	defaultLog.Write(context.Background(), FATAL, relatedId, fmt.Sprintf(format, a...))
}

func FatalCtx(ctx context.Context, relatedId string, msg interface{}, fields ...Field) {
	defaultLog.Write(ctx, FATAL, relatedId, MsgMarshal(msg), fields...)
}

func FatalfCtx(ctx context.Context, relatedId, format string, a ...any) {
	defaultLog.Write(ctx, FATAL, relatedId, fmt.Sprintf(format, a...))
}

// ----------- severe log ---------------

func Severe(relatedId string, msg interface{}, fields ...Field) {
	defaultLog.Write(context.Background(), SEVERE, relatedId, MsgMarshal(msg), fields...)
}

func Severef(relatedId, format string, a ...any) {
	defaultLog.Write(context.Background(), SEVERE, relatedId, fmt.Sprintf(format, a...))
}

func SevereCtx(ctx context.Context, relatedId string, msg interface{}, fields ...Field) {
	defaultLog.Write(ctx, SEVERE, relatedId, MsgMarshal(msg), fields...)
}

func SeverefCtx(ctx context.Context, relatedId, format string, a ...any) {
	defaultLog.Write(ctx, SEVERE, relatedId, fmt.Sprintf(format, a...))
}

// ----------- alert log ---------------

// 记录Debug日志：在生产环境下不输出信息
func Alert(relatedId string, msg interface{}, fields ...Field) {
	defaultLog.Write(context.Background(), ALERT, relatedId, MsgMarshal(msg), fields...)
}

// 记录Debug日志： 在生产环境下不输出信息
func Alertf(relatedId, format string, a ...any) {
	defaultLog.Write(context.Background(), ALERT, relatedId, fmt.Sprintf(format, a...))
}

func AlertCtx(ctx context.Context, relatedId string, msg interface{}, fields ...Field) {
	defaultLog.Write(ctx, ALERT, relatedId, MsgMarshal(msg), fields...)
}

func AlertfCtx(ctx context.Context, relatedId, format string, a ...any) {
	defaultLog.Write(ctx, ALERT, relatedId, fmt.Sprintf(format, a...))
}

func MsgMarshal(v interface{}) string {
	switch v.(type) {
	case bool:
		if v.(bool) {
			return "true"
		}
		return "false"
	case string:
		return v.(string)
	case int8:
		return strconv.Itoa(int(v.(int8)))
	case int16:
		return strconv.Itoa(int(v.(int16)))
	case int32, int:
		return strconv.Itoa(int(v.(int32)))
	case int64:
		return strconv.Itoa(int(v.(int64)))
	case uint8:
		return strconv.Itoa(int(v.(uint8)))
	case uint16:
		return strconv.Itoa(int(v.(uint16)))
	case uint32:
		return strconv.Itoa(int(v.(uint32)))
	case uint64:
		return strconv.Itoa(int(v.(uint64)))
	case float32:
		return strconv.FormatFloat(float64(v.(float32)), 'f', 10, 32)
	case float64:
		return strconv.FormatFloat(v.(float64), 'f', 1, 64)
	case time.Time:
		return v.(time.Time).String()
	case []byte:
		return string(v.([]byte))
	}

	if b, e := json.Marshal(v); e == nil {
		return string(b)
	}

	return fmt.Sprintf("%v", v)
}
